package cn.metona.utils;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
import java.util.Locale;

public final class DateUtils {

    // 私有构造方法，防止实例化
    private DateUtils() {
        throw new UnsupportedOperationException("Utility class");
    }

    // 默认日期格式
    private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    private static final String DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

    /**
     * 将日期格式化为字符串
     *
     * @param date 日期
     * @return 格式化后的日期字符串
     */
    public static String formatDate(LocalDate date) {
        return formatDate(date, DEFAULT_DATE_FORMAT);
    }

    /**
     * 将日期格式化为字符串
     *
     * @param date   日期
     * @param format 日期格式
     * @return 格式化后的日期字符串
     */
    public static String formatDate(LocalDate date, String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return date.format(formatter);
    }

    /**
     * 将日期时间格式化为字符串
     *
     * @param dateTime 日期时间
     * @return 格式化后的日期时间字符串
     */
    public static String formatDateTime(LocalDateTime dateTime) {
        return formatDateTime(dateTime, DEFAULT_DATETIME_FORMAT);
    }

    /**
     * 将日期时间格式化为字符串
     *
     * @param dateTime 日期时间
     * @param format   日期时间格式
     * @return 格式化后的日期时间字符串
     */
    public static String formatDateTime(LocalDateTime dateTime, String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return dateTime.format(formatter);
    }

    /**
     * 将字符串解析为日期
     *
     * @param dateStr 日期字符串
     * @return 解析后的日期
     */
    public static LocalDate parseDate(String dateStr) {
        return parseDate(dateStr, DEFAULT_DATE_FORMAT);
    }

    /**
     * 将字符串解析为日期
     *
     * @param dateStr 日期字符串
     * @param format  日期格式
     * @return 解析后的日期
     */
    public static LocalDate parseDate(String dateStr, String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return LocalDate.parse(dateStr, formatter);
    }

    /**
     * 将字符串解析为日期时间
     *
     * @param dateTimeStr 日期时间字符串
     * @return 解析后的日期时间
     */
    public static LocalDateTime parseDateTime(String dateTimeStr) {
        return parseDateTime(dateTimeStr, DEFAULT_DATETIME_FORMAT);
    }

    /**
     * 将字符串解析为日期时间
     *
     * @param dateTimeStr 日期时间字符串
     * @param format      日期时间格式
     * @return 解析后的日期时间
     */
    public static LocalDateTime parseDateTime(String dateTimeStr, String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return LocalDateTime.parse(dateTimeStr, formatter);
    }

    /**
     * 计算两个日期之间的天数差
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 天数差
     */
    public static long daysBetween(LocalDate startDate, LocalDate endDate) {
        return ChronoUnit.DAYS.between(startDate, endDate);
    }

    /**
     * 计算两个日期之间的月数差
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 月数差
     */
    public static long monthsBetween(LocalDate startDate, LocalDate endDate) {
        return ChronoUnit.MONTHS.between(startDate, endDate);
    }

    /**
     * 计算两个日期之间的年数差
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 年数差
     */
    public static long yearsBetween(LocalDate startDate, LocalDate endDate) {
        return ChronoUnit.YEARS.between(startDate, endDate);
    }

    /**
     * 在日期上添加指定的天数
     *
     * @param date 日期
     * @param days 天数
     * @return 添加天数后的日期
     */
    public static LocalDate addDays(LocalDate date, long days) {
        return date.plusDays(days);
    }

    /**
     * 在日期上减去指定的天数
     *
     * @param date 日期
     * @param days 天数
     * @return 减去天数后的日期
     */
    public static LocalDate subtractDays(LocalDate date, long days) {
        return date.minusDays(days);
    }

    /**
     * 获取当前日期
     *
     * @return 当前日期
     */
    public static LocalDate getCurrentDate() {
        return LocalDate.now();
    }

    /**
     * 获取当前日期时间
     *
     * @return 当前日期时间
     */
    public static LocalDateTime getCurrentDateTime() {
        return LocalDateTime.now();
    }

    /**
     * 获取某个月的第一天
     *
     * @param date 日期
     * @return 该月的第一天
     */
    public static LocalDate getFirstDayOfMonth(LocalDate date) {
        return date.with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 获取某个月的最后一天
     *
     * @param date 日期
     * @return 该月的最后一天
     */
    public static LocalDate getLastDayOfMonth(LocalDate date) {
        return date.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 判断日期是否在某个范围内
     *
     * @param date  日期
     * @param start 开始日期
     * @param end   结束日期
     * @return 是否在范围内
     */
    public static boolean isDateInRange(LocalDate date, LocalDate start, LocalDate end) {
        return !date.isBefore(start) && !date.isAfter(end);
    }

    /**
     * 获取日期的开始时间（当天的 00:00:00）
     *
     * @param date 日期
     * @return 日期的开始时间
     */
    public static LocalDateTime getStartOfDay(LocalDate date) {
        return date.atStartOfDay();
    }

    /**
     * 获取日期的结束时间（当天的 23:59:59）
     *
     * @param date 日期
     * @return 日期的结束时间
     */
    public static LocalDateTime getEndOfDay(LocalDate date) {
        return date.atTime(LocalTime.MAX);
    }

    /**
     * 判断是否为闰年
     *
     * @param date 日期
     * @return 是否为闰年
     */
    public static boolean isLeapYear(LocalDate date) {
        return date.isLeapYear();
    }

    /**
     * 获取当前日期的季度
     *
     * @param date 日期
     * @return 季度（1-4）
     */
    public static int getQuarter(LocalDate date) {
        int month = date.getMonthValue();
        return (month - 1) / 3 + 1;
    }

    /**
     * 比较两个日期的先后
     *
     * @param date1 第一个日期
     * @param date2 第二个日期
     * @return 如果 date1 在 date2 之前，返回 -1；如果相等，返回 0；否则返回 1
     */
    public static int compareDates(LocalDate date1, LocalDate date2) {
        return date1.compareTo(date2);
    }

    /**
     * 获取当前日期的周数
     *
     * @param date 日期
     * @return 周数
     */
    public static int getWeekOfYear(LocalDate date) {
        WeekFields weekFields = WeekFields.of(Locale.getDefault());
        return date.get(weekFields.weekOfWeekBasedYear());
    }

    /**
     * 获取当前日期的星期几
     *
     * @param date 日期
     * @return 星期几
     */
    public static DayOfWeek getDayOfWeek(LocalDate date) {
        return date.getDayOfWeek();
    }

    /**
     * 获取两个日期之间的工作日（排除周末）
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 工作日天数
     */
    public static long getWorkingDays(LocalDate startDate, LocalDate endDate) {
        long days = ChronoUnit.DAYS.between(startDate, endDate);
        long workingDays = 0;
        for (int i = 0; i <= days; i++) {
            LocalDate currentDate = startDate.plusDays(i);
            if (currentDate.getDayOfWeek() != DayOfWeek.SATURDAY && currentDate.getDayOfWeek() != DayOfWeek.SUNDAY) {
                workingDays++;
            }
        }
        return workingDays;
    }

    /**
     * 获取当前日期的年龄
     *
     * @param birthDate 出生日期
     * @return 年龄
     */
    public static int getAge(LocalDate birthDate) {
        return (int) ChronoUnit.YEARS.between(birthDate, LocalDate.now());
    }

    /**
     * 判断两个日期是否是同一天
     *
     * @param date1 第一个日期
     * @param date2 第二个日期
     * @return 是否是同一天
     */
    public static boolean isSameDay(LocalDate date1, LocalDate date2) {
        return date1.isEqual(date2);
    }

    /**
     * 获取当前日期的毫秒数
     *
     * @param date 日期
     * @return 毫秒数
     */
    public static long toEpochMilli(LocalDate date) {
        return date.atStartOfDay().toEpochSecond(java.time.ZoneOffset.UTC) * 1000;
    }

    /**
     * 将毫秒数转换为日期
     *
     * @param epochMilli 毫秒数
     * @return 日期
     */
    public static LocalDate fromEpochMilli(long epochMilli) {
        return LocalDate.ofEpochDay(epochMilli / 86400000);
    }

    /**
     * 获取当前日期的月份天数
     *
     * @param date 日期
     * @return 月份天数
     */
    public static int getDaysInMonth(LocalDate date) {
        return YearMonth.from(date).lengthOfMonth();
    }

    /**
     * 获取当前日期的季度开始日期
     *
     * @param date 日期
     * @return 季度开始日期
     */
    public static LocalDate getStartOfQuarter(LocalDate date) {
        int quarter = getQuarter(date);
        int startMonth = (quarter - 1) * 3 + 1;
        return date.withMonth(startMonth).with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 获取当前日期的季度结束日期
     *
     * @param date 日期
     * @return 季度结束日期
     */
    public static LocalDate getEndOfQuarter(LocalDate date) {
        int quarter = getQuarter(date);
        int endMonth = quarter * 3;
        return date.withMonth(endMonth).with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 获取当前日期的年份开始日期
     *
     * @param date 日期
     * @return 年份开始日期
     */
    public static LocalDate getStartOfYear(LocalDate date) {
        return date.with(TemporalAdjusters.firstDayOfYear());
    }

    /**
     * 获取当前日期的年份结束日期
     *
     * @param date 日期
     * @return 年份结束日期
     */
    public static LocalDate getEndOfYear(LocalDate date) {
        return date.with(TemporalAdjusters.lastDayOfYear());
    }

    public static void main(String[] args) {
        // 示例用法
        LocalDate today = getCurrentDate();
        System.out.println("Today: " + formatDate(today));

        int weekOfYear = getWeekOfYear(today);
        System.out.println("Week of year: " + weekOfYear);

        DayOfWeek dayOfWeek = getDayOfWeek(today);
        System.out.println("Day of week: " + dayOfWeek);

        LocalDate startDate = LocalDate.of(2023, 10, 1);
        LocalDate endDate = LocalDate.of(2023, 10, 31);
        long workingDays = getWorkingDays(startDate, endDate);
        System.out.println("Working days between " + formatDate(startDate) + " and " + formatDate(endDate) + ": " + workingDays);

        LocalDate birthDate = LocalDate.of(1990, 1, 1);
        int age = getAge(birthDate);
        System.out.println("Age: " + age);

        boolean isSameDay = isSameDay(today, today);
        System.out.println("Is same day? " + isSameDay);

        long epochMilli = toEpochMilli(today);
        System.out.println("Epoch millis: " + epochMilli);

        LocalDate fromEpochMilli = fromEpochMilli(epochMilli);
        System.out.println("Date from epoch millis: " + formatDate(fromEpochMilli));

        int daysInMonth = getDaysInMonth(today);
        System.out.println("Days in month: " + daysInMonth);

        LocalDate startOfQuarter = getStartOfQuarter(today);
        System.out.println("Start of quarter: " + formatDate(startOfQuarter));

        LocalDate endOfQuarter = getEndOfQuarter(today);
        System.out.println("End of quarter: " + formatDate(endOfQuarter));

        LocalDate startOfYear = getStartOfYear(today);
        System.out.println("Start of year: " + formatDate(startOfYear));

        LocalDate endOfYear = getEndOfYear(today);
        System.out.println("End of year: " + formatDate(endOfYear));
    }
}
